#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>

#include <tokenhelper.h>
#include <reservewords.h>

#include <string>

enum {
    TK_NONE = 0,

    TK_IGNORE,

    TK_IDENT,

    TK_LEFTP,

    TK_RIGHTP,
};

// stream
static FILE                    *_fp;

static std::string              _fn;

static int                      _lineno;

static std::string              _tk_buf;

static int _indent_table[256] = {
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,
    0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
    1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,
    0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
    1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
};

static int _indent_head_table[256] = {
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
    1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,
    0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
    1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
};

static int
_fgetc()
{
    static int r = 0;

    if (_fp) {
        int c = fgetc(_fp);

        switch (c) {
        case '\r':
            ++_lineno;
            r = 1;
            break;

        case '\n':
            if (!r)
                ++_lineno;
            else
                r = 0;
            break;

        default:
            r = 0;
            break;
        }

        return c;
    }

    return EOF;
}

#define HANDLE_LINE_END()                       \
    do {                                        \
        switch (c) {                            \
        case '\r':                              \
            c = _fgetc();                       \
            if (c != EOF) {                     \
                if (c != '\n')                  \
                    ungetc(c, _fp);             \
            }                                   \
            return 0;                           \
                                                \
        case '\n':                              \
            return 0;                           \
        }                                       \
    } while (0)

// python 字符串
// 一般的字符串用单双引号，如果是多行的，用三个单双引号引起来
// unicode 字符串，前面加上 `u' 或者 `U'
// 原始字符串(比如说不要让转义字符生效)，前面加上 `r' 或者 `R'

// 单引号或者双引号引起来的字符串
static int
_skip_string(int left)
{
    if (_fp) {
        int c;

        while ((c = _fgetc()) != EOF) {
            if (c == '\\')
                (void) _fgetc();
            else {
                if (c == left)
                    break;
            }
        }
    }

    return 0;
}

// 块字符串
static int
_skip_block_string(int left)
{
    int c;
    while ((c = _fgetc()) != EOF) {
        if (c == '\\')
            (void) _fgetc();
        else {
            if (c == left) {
                int i, j;
                i = _fgetc();
                j = _fgetc();
                if (i == left && j == left)
                    break;
            }
        }
    }

    return 0;
}

// skip comment
static int
_skip_comment()
{
    int c;

    // python 只有单行注释
    while ((c = _fgetc()) != EOF) {
        HANDLE_LINE_END();
    }

    return 0;
}

#define SKIP_STRING()                               \
    do {                                            \
        i = _fgetc();                               \
        j = _fgetc();                               \
        if (i == c && j == c)                       \
            _skip_block_string(c);                  \
        else {                                      \
            if (j != EOF && j != '\r' && j != '\n') \
                ungetc(j, _fp);                     \
            if (i != EOF && i != '\r' && i != '\n') \
                ungetc(i, _fp);                     \
            _skip_string(c);                        \
        }                                           \
    } while (0)

static int
_next_token(int *lineno)
{
    if (!_fp)
        return TK_NONE;

    _tk_buf.clear();

    int c;
    int i, j;
    for (;;) {
        c = _fgetc();

        switch (c) {
        default:
            if (_indent_head_table[c]) {
                // 查看是否为 r R u U 接着 ' "
                i = _fgetc();
                if ((i == '\'' || i == '"')
                    && (c == 'r' || c == 'R' || c == 'u' || c == 'U')) {
                    c = i;
                    SKIP_STRING();
                    return TK_IGNORE;
                }

                _tk_buf.push_back(c);

                if (lineno)
                    *lineno = _lineno;

                c = i;
                while (c != EOF) {
                    if (_indent_table[c]) {
                        _tk_buf.push_back(c);
                    }
                    else {
                        if (c != EOF && c != '\r' && c != '\n')
                            ungetc(c, _fp);
                        break;
                    }

                    c = _fgetc();
                }

                // 检查是否为关键字
                Keyword *kw = in_word_set(_tk_buf.c_str(), _tk_buf.size());
                if (kw)
                    return kw->value;

                return TK_IDENT;
            }
            else
                return TK_IGNORE;
            break;

        case ' ':
        case '\t':
        case '\r':
        case '\n':
        case '\f':
            break;

        case '"':
        case '\'':
            SKIP_STRING();
            return TK_IGNORE;
            break;

        case '#':
            _skip_comment();
            break;

        case '(':
            return TK_LEFTP;
            break;

        case ')':
            return TK_RIGHTP;
            break;

        case EOF:
            return TK_NONE;
            break;
        }
    }

    // never go here
    return TK_NONE;
}

// interface
int tk_open_file(const char *fn)
{
    if (_fp)
        fclose(_fp);

    _fp = fopen(fn, "r");

    _lineno = 1;

    _fn = fn;

    return _fp ? 0 : -1;
}

int tk_close_file()
{
    if (_fp) {
        fclose(_fp);
        _fp = NULL;
    }

    return 0;
}

int tk_fetch_next(int *lineno, std::string& token)
{
    int curr_tk_type;
    int curr_tk_line;
    int next_tk_type;
    int next_tk_lineno;

    curr_tk_type = TK_NONE;

    for (;;) {
        if (curr_tk_type == TK_NONE) {
            if ((curr_tk_type = _next_token(&curr_tk_line)) == TK_NONE)
                break;
        }

        switch (curr_tk_type) {
        case TK_IDENT:
            token = _tk_buf;
            next_tk_type = _next_token(&next_tk_lineno);
            if (next_tk_type == TK_LEFTP) {
                *lineno = curr_tk_line;
                return TT_REF;
            }
            curr_tk_line = next_tk_lineno;
            curr_tk_type = next_tk_type;
            break;
        case TK_DEF:
        case TK_CLASS:
            next_tk_type = _next_token(&next_tk_lineno);
            if (next_tk_type == TK_IDENT) {
                token = _tk_buf;
                *lineno = next_tk_lineno;
                return ((curr_tk_type == TK_DEF)
                        ? (TT_FUNC)
                        : (TT_CLASS));
            }
            curr_tk_line = next_tk_lineno;
            curr_tk_type = next_tk_type;
            break;
        default:
            curr_tk_type = TK_NONE;
            break;
        }
    }

    return TT_NONE;
}
